package pl.edu.agh.bp;

import pl.edu.agh.bp.momentum.ConstantMomentum;
import pl.edu.agh.cp.CounterPropagationNetwork;
import pl.edu.agh.cp.GrossbergLayer;
import pl.edu.agh.cp.learning.DeltaRuleLearning;
import pl.edu.agh.neural.core.*;
import pl.edu.agh.neural.input.BpNetworkBuilder;
import pl.edu.agh.neural.input.IWeightsGenerator;
import pl.edu.agh.neural.input.RandomWeightsGenerator;
import pl.edu.agh.neural.input.WeightsFromFileGenerator;
import pl.edu.agh.som.KohonenMap;
import pl.edu.agh.som.conscience.DefaultConscience;
import pl.edu.agh.som.conscience.IConscienceFunction;
import pl.edu.agh.som.distance.EuclideanDistance;
import pl.edu.agh.som.learning.ConstantLearning;
import pl.edu.agh.som.learning.LinearLearning;
import pl.edu.agh.som.neighborhood.INeighborhoodFunction;
import pl.edu.agh.som.neighborhood.SquareNeighborhood;
import pl.edu.agh.som.winfrequency.DefaultWinFrequencyUpdateFunction;

import javax.swing.text.BoxView;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import static com.google.common.collect.Lists.newArrayList;

public class RunnerBp {

    public static void main(String[] args) throws Exception{

        // train-----------------------------------------------------------
        List<String> lines = Files.readAllLines(Paths.get("trainBP.txt"), Charset.forName("UTF-8"));
        int inputs = Integer.valueOf(lines.get(0));
        int outputs = Integer.valueOf(lines.get(1));
        int n = Integer.valueOf(lines.get(2));
        int learningSteps = Integer.valueOf(lines.get(3));

        double[][] dataVectors = readVectors(lines, 4, n, inputs);
        double[][] expectedVectors = readVectors(lines, 4 + n, n, outputs);

        IWeightsGenerator weightsGenerator = new RandomWeightsGenerator();
        //IWeightsGenerator weightsGenerator = new WeightsFromFileGenerator("w.txt");
        ITrainableNetworkWithTeacher bpNetwork = BpNetworkBuilder.BuildFromFile("bpNetwork.txt", weightsGenerator);

        System.out.println("Initial network weights: ");
        for (ILayer layer : bpNetwork.getLayers()) {
            for (INeuron neuron : layer.getNeurons()) {
                for (double weight : neuron.getInputConnectionsWeights()) {
                    System.out.printf("%.3f ", weight);
                }
                System.out.println();
            }
        }

        bpNetwork.train(dataVectors, expectedVectors, learningSteps, new ConstantLearning(0.2), new ConstantMomentum(0.5));

        List<ILayer> layers = bpNetwork.getLayers();
        ILayer outputLayer = layers.get(layers.size() - 1);

        System.out.println("Weights: ");
        for (INeuron neuron : outputLayer.getNeurons()) {
            for (double weight : neuron.getInputConnectionsWeights()) {
                System.out.printf("%.1f ", weight);
            }
            System.out.println();
        }

        System.out.println();
        System.out.println("Results: ");
        for (double[] dataVector : dataVectors) {
            double[] result = bpNetwork.evaluate(dataVector);
            for (Double value : result) {
                System.out.printf("%.3f ", value);
            }
            System.out.println();
        }
    }

    private static double[][] readVectors(List<String> lines, int startIndex, int n, int dim)
    {
        double[][] dataVectors = new double[n][];

        for (int i = 0; i < n; i++)
        {
            String[] vals = lines.get(i + startIndex).split("\\s+");
            dataVectors[i] = new double[dim];
            for (int j = 0; j < dim; j++)
            {
                dataVectors[i][j] = Double.parseDouble(vals[j]);
            }
        }

        return dataVectors;
    }
}
